// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using iPanel.DataProvider;
using iPanel.Model.Otlp;

namespace iPanel.Otlp.Model;

/// <summary>
/// Represents a Span within an Operation (Trace)
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
public class OtlpSpan
{
    /// <summary>
    /// 表示 peer.service 属性的键名，用于标识服务调用的对等方服务。
    /// </summary>
    public const string PeerServiceAttributeKey = "peer.service";
    /// <summary>
    /// 表示 url.full 属性的键名，用于存储完整的URL信息。
    /// </summary>
    public const string UrlFullAttributeKey = "url.full";
    /// <summary>
    /// 表示 server.address 属性的键名，用于记录服务器的地址。
    /// </summary>
    public const string ServerAddressAttributeKey = "server.address";
    /// <summary>
    /// 表示 server.port 属性的键名，用于记录服务器的端口号。
    /// </summary>
    public const string ServerPortAttributeKey = "server.port";
    /// <summary>
    /// 表示 net.peer.name 属性的键名，用于记录网络对等方的名称。
    /// </summary>
    public const string NetPeerNameAttributeKey = "net.peer.name";
    /// <summary>
    /// 表示 net.peer.port 属性的键名，用于记录网络对等方的端口号。
    /// </summary>
    public const string NetPeerPortAttributeKey = "net.peer.port";
    /// <summary>
    /// 表示 span.kind 属性的键名，用于标识跨度的类型。
    /// </summary>
    public const string SpanKindAttributeKey = "span.kind";

    /// <summary>
    /// 获取当前跨度所属跟踪的唯一标识符。
    /// </summary>
    public string TraceId => Trace.TraceId;
    /// <summary>
    /// 获取当前跨度所属的跟踪对象。
    /// </summary>
    public OtlpTrace Trace { get; }
    /// <summary>
    /// 获取当前跨度的来源应用程序视图。
    /// </summary>
    public OtlpApplicationView Source { get; }

    /// <summary>
    /// 获取或初始化当前跨度的唯一标识符。
    /// </summary>
    public required string SpanId { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的父跨度的唯一标识符，如果没有父跨度则为 null。
    /// </summary>
    public required string? ParentSpanId { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的名称。
    /// </summary>
    public required string Name { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的类型。
    /// </summary>
    public required OtlpSpanKind Kind { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的开始时间。
    /// </summary>
    public required DateTime StartTime { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的结束时间。
    /// </summary>
    public required DateTime EndTime { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的状态码。
    /// </summary>
    public required OtlpSpanStatusCode Status { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的状态消息，如果没有则为 null。
    /// </summary>
    public required string? StatusMessage { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的状态信息，如果没有则为 null。
    /// </summary>
    public required string? State { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的属性集合，以键值对数组形式存储。
    /// </summary>
    public required KeyValuePair<string, string>[] Attributes { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度包含的事件列表。
    /// </summary>
    public required List<OtlpSpanEvent> Events { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的链接列表。
    /// </summary>
    public required List<OtlpSpanLink> Links { get; init; }
    /// <summary>
    /// 获取或初始化当前跨度的反向链接列表。
    /// </summary>
    public required List<OtlpSpanLink> BackLinks { get; init; }

    /// <summary>
    /// 获取当前跨度的作用域信息。
    /// </summary>
    public OtlpScope Scope { get; }
    /// <summary>
    /// 获取当前跨度的持续时间，即结束时间与开始时间的差值。
    /// </summary>
    public TimeSpan Duration => EndTime - StartTime;

    /// <summary>
    /// 获取当前跨度的子跨度集合。
    /// </summary>
    /// <returns>当前跨度的子跨度集合。</returns>
    public IEnumerable<OtlpSpan> GetChildSpans() => GetChildSpans(this, Trace.Spans);
    /// <summary>
    /// 获取指定父跨度的子跨度集合。
    /// </summary>
    /// <param name="parentSpan">父跨度对象。</param>
    /// <param name="spans">跨度集合。</param>
    /// <returns>指定父跨度的子跨度集合。</returns>
    public static IEnumerable<OtlpSpan> GetChildSpans(OtlpSpan parentSpan, OtlpSpanCollection spans) => spans.Where(s => s.ParentSpanId == parentSpan.SpanId);

    /// <summary>
    /// 缓存的显示摘要信息，用于提高性能。
    /// </summary>
    private string? _cachedDisplaySummary;

    /// <summary>
    /// 获取当前跨度的父跨度对象。
    /// </summary>
    /// <returns>当前跨度的父跨度对象，如果没有父跨度则为 null。</returns>
    public OtlpSpan? GetParentSpan()
    {
        // 如果父跨度ID为空，则返回 null
        if (string.IsNullOrEmpty(ParentSpanId))
        {
            return null;
        }

        // 尝试从跟踪的跨度集合中获取父跨度
        if (Trace.Spans.TryGetValue(ParentSpanId, out var span))
        {
            return span;
        }

        return null;
    }

    /// <summary>
    /// 构造函数，初始化 OtlpSpan 对象。
    /// </summary>
    /// <param name="applicationView">跨度的来源应用程序视图。</param>
    /// <param name="trace">跨度所属的跟踪对象。</param>
    /// <param name="scope">跨度的作用域信息。</param>
    public OtlpSpan(OtlpApplicationView applicationView, OtlpTrace trace, OtlpScope scope)
    {
        Source = applicationView;
        Trace = trace;
        Scope = scope;
    }

    /// <summary>
    /// 克隆一个 OtlpSpan 对象。
    /// </summary>
    /// <param name="item">要克隆的 OtlpSpan 对象。</param>
    /// <param name="trace">克隆后的跨度所属的跟踪对象。</param>
    /// <returns>克隆后的 OtlpSpan 对象。</returns>
    public static OtlpSpan Clone(OtlpSpan item, OtlpTrace trace)
    {
        return new OtlpSpan(item.Source, trace, item.Scope)
        {
            SpanId = item.SpanId,
            ParentSpanId = item.ParentSpanId,
            Name = item.Name,
            Kind = item.Kind,
            StartTime = item.StartTime,
            EndTime = item.EndTime,
            Status = item.Status,
            StatusMessage = item.StatusMessage,
            State = item.State,
            Attributes = item.Attributes,
            Events = item.Events,
            Links = item.Links,
            BackLinks = item.BackLinks,
        };
    }

    /// <summary>
    /// 获取当前跨度的所有属性信息。
    /// </summary>
    /// <returns>当前跨度的所有属性信息列表。</returns>
    public List<OtlpDisplayField> AllProperties()
    {
        var props = new List<OtlpDisplayField>
        {
            // 添加 SpanId 属性
            new OtlpDisplayField { DisplayName = "SpanId", Key = KnownTraceFields.SpanIdField, Value = SpanId },
            // 添加 Name 属性
            new OtlpDisplayField { DisplayName = "Name", Key = KnownTraceFields.NameField, Value = Name },
            // 添加 Kind 属性
            new OtlpDisplayField { DisplayName = "Kind", Key = KnownTraceFields.KindField, Value = Kind.ToString() },
        };

        // 如果状态码不为 Unset，则添加 Status 属性
        if (Status != OtlpSpanStatusCode.Unset)
        {
            props.Add(new OtlpDisplayField { DisplayName = "Status", Key = KnownTraceFields.StatusField, Value = Status.ToString() });
        }

        // 如果状态消息不为空，则添加 StatusMessage 属性
        if (!string.IsNullOrEmpty(StatusMessage))
        {
            props.Add(new OtlpDisplayField { DisplayName = "StatusMessage", Key = KnownTraceFields.StatusMessageField, Value = StatusMessage });
        }

        // 遍历属性集合，按键排序后添加到属性列表中
        foreach (var kv in Attributes.OrderBy(a => a.Key))
        {
            props.Add(new OtlpDisplayField { DisplayName = kv.Key, Key = $"unknown-{kv.Key}", Value = kv.Value });
        }

        return props;
    }

    /// <summary>
    /// 用于调试时显示跨度的详细信息。
    /// </summary>
    /// <returns>包含跨度ID、开始时间、父跨度ID和跟踪ID的调试信息字符串。</returns>
    private string DebuggerToString()
    {
        return $@"SpanId = {SpanId}, StartTime = {StartTime.ToLocalTime():h:mm:ss.fff tt}, ParentSpanId = {ParentSpanId}, TraceId = {Trace.TraceId}";
    }

    /// <summary>
    /// 获取当前跨度的显示摘要信息。
    /// </summary>
    /// <returns>当前跨度的显示摘要信息。</returns>
    public string GetDisplaySummary()
    {
        // 如果缓存的显示摘要信息为空，则调用 BuildDisplaySummary 方法生成摘要信息并缓存
        return _cachedDisplaySummary ??= BuildDisplaySummary(this);

        /// <summary>
        /// 静态方法，用于构建跨度的显示摘要信息。
        /// 优先根据常见场景（如HTTP、RPC、DATA等）生成摘要信息，若无法匹配则使用跨度名称。
        /// </summary>
        /// <param name="span">要构建摘要信息的跨度对象。</param>
        /// <returns>构建好的显示摘要信息。</returns>
        static string BuildDisplaySummary(OtlpSpan span)
        {
            // 对于客户端、生产者或消费者类型的跨度
            if (span.Kind is OtlpSpanKind.Client or OtlpSpanKind.Producer or OtlpSpanKind.Consumer)
            {
                // 如果存在 http.method 属性
                if (!string.IsNullOrEmpty(OtlpHelpers.GetValue(span.Attributes, "http.method")))
                {
                    // 获取 HTTP 方法和状态码
                    var httpMethod = OtlpHelpers.GetValue(span.Attributes, "http.method");
                    var statusCode = OtlpHelpers.GetValue(span.Attributes, "http.status_code");

                    // 返回 HTTP 请求的摘要信息
                    return $"HTTP {httpMethod?.ToUpperInvariant()} {statusCode}";
                }
                // 如果存在 db.system 属性
                else if (!string.IsNullOrEmpty(OtlpHelpers.GetValue(span.Attributes, "db.system")))
                {
                    // 获取数据库系统名称
                    var dbSystem = OtlpHelpers.GetValue(span.Attributes, "db.system");

                    // 返回数据库操作的摘要信息
                    return $"DATA {dbSystem} {span.Name}";
                }
                // 如果存在 rpc.system 属性
                else if (!string.IsNullOrEmpty(OtlpHelpers.GetValue(span.Attributes, "rpc.system")))
                {
                    // 获取 RPC 系统、服务和方法名称
                    var rpcSystem = OtlpHelpers.GetValue(span.Attributes, "rpc.system");
                    var rpcService = OtlpHelpers.GetValue(span.Attributes, "rpc.service");
                    var rpcMethod = OtlpHelpers.GetValue(span.Attributes, "rpc.method");

                    // 如果是 gRPC 服务
                    if (string.Equals(rpcSystem, "grpc", StringComparison.OrdinalIgnoreCase))
                    {
                        // 获取 gRPC 状态码
                        var grpcStatusCode = OtlpHelpers.GetValue(span.Attributes, "rpc.grpc.status_code");

                        // 构建 RPC 请求的摘要信息
                        var summary = $"RPC {rpcService}/{rpcMethod}";
                        // 如果存在 gRPC 状态码且能解析为枚举类型
                        if (!string.IsNullOrEmpty(grpcStatusCode) && Enum.TryParse<StatusCode>(grpcStatusCode, out var statusCode))
                        {
                            // 在摘要信息中添加状态码
                            summary += $" {statusCode}";
                        }

                        return summary;
                    }

                    // 返回普通 RPC 请求的摘要信息
                    return $"RPC {rpcService}/{rpcMethod}";
                }
                // 如果存在 messaging.system 属性
                else if (!string.IsNullOrEmpty(OtlpHelpers.GetValue(span.Attributes, "messaging.system")))
                {
                    // 获取消息系统、操作和目标名称
                    var messagingSystem = OtlpHelpers.GetValue(span.Attributes, "messaging.system");
                    var messagingOperation = OtlpHelpers.GetValue(span.Attributes, "messaging.operation");
                    var destinationName = OtlpHelpers.GetValue(span.Attributes, "messaging.destination.name");

                    // 返回消息操作的摘要信息
                    return $"MSG {messagingSystem} {messagingOperation} {destinationName}";
                }
            }

            // 如果以上条件都不满足，则返回跨度名称作为摘要信息
            return span.Name;
        }
    }

    /// <summary>
    /// 获取指定跨度的指定字段的值。
    /// </summary>
    /// <param name="span">要获取字段值的跨度对象。</param>
    /// <param name="field">要获取的字段名称。</param>
    /// <returns>指定字段的值，如果未找到则返回 null。</returns>
    public static string? GetFieldValue(OtlpSpan span, string field)
    {
        return field switch
        {
            // 如果是服务名称字段，返回跨度来源应用程序的名称
            KnownResourceFields.ServiceNameField => span.Source.Application.ApplicationName,
            // 如果是跟踪ID字段，返回跨度所属跟踪的ID
            KnownTraceFields.TraceIdField => span.TraceId,
            // 如果是跨度ID字段，返回跨度的ID
            KnownTraceFields.SpanIdField => span.SpanId,
            // 如果是跨度类型字段，返回跨度类型的字符串表示
            KnownTraceFields.KindField => span.Kind.ToString(),
            // 如果是状态字段，返回跨度状态的字符串表示
            KnownTraceFields.StatusField => span.Status.ToString(),
            // 如果是来源名称字段，返回跨度作用域的名称
            KnownSourceFields.NameField => span.Scope.ScopeName,
            // 如果是名称字段，返回跨度的名称
            KnownTraceFields.NameField => span.Name,
            // 其他情况，从跨度属性中获取字段值
            _ => span.Attributes.GetValue(field)
        };
    }

    public static string? GetFieldName(string field)
    {
        return field switch
        {
            // 如果是服务名称字段，返回跨度来源应用程序的名称
            KnownResourceFields.ServiceNameField => nameof(OtlpSpanEntity.Application),
            // 如果是跟踪ID字段，返回跨度所属跟踪的ID
            KnownTraceFields.TraceIdField => nameof(OtlpSpanEntity.TraceId),
            // 如果是跨度ID字段，返回跨度的ID
            KnownTraceFields.SpanIdField => nameof(OtlpSpanEntity.SpanId),
            // 如果是跨度类型字段，返回跨度类型的字符串表示
            KnownTraceFields.KindField => nameof(OtlpSpanEntity.Kind),
            // 如果是状态字段，返回跨度状态的字符串表示
            KnownTraceFields.StatusField => nameof(OtlpSpanEntity.State),
            // 如果是来源名称字段，返回跨度作用域的名称
            KnownSourceFields.NameField => nameof(OtlpSpanEntity.Name),
            // 如果是名称字段，返回跨度的名称
            KnownTraceFields.NameField => nameof(OtlpSpanEntity.Application),
            // 其他情况，从跨度属性中获取字段值
            _ => null
        };
    }
}
